﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text.RegularExpressions;
using Dapper;
using DapperExtensions;
using DapperExtensions.Mapper;

namespace ZilLion.Service.Core.DapperWrapper
{
    /// <summary>
    ///     Dapper基本操作的封装，仓库
    /// </summary>
    /// <typeparam name="TEntity"></typeparam>
    public class Respository<TEntity> : IRespository where TEntity : class
    {
        protected IDbContext Context;

        public Respository()
        {
        }


        public Respository(IDbContext source)
        {
            Context = source;
        }

        protected void SetDbContext(IDbContext DbContext)
        {
            this.Context = DbContext;
        }


        public ClassMapper<TEntity> GetMap()
        {
            return DapperExtensions.DapperExtensions.GetMap<TEntity>() as ClassMapper<TEntity>;
        }

          #region 增删改

        /// <summary>
        ///     添加实体
        /// </summary>
        /// <param name="item"></param>
        /// <param name="unitOfWork"></param>
        /// <returns></returns>
        protected dynamic Add(TEntity item, UnitOfWork unitOfWork = null)
        {
            if (unitOfWork == null ||
                unitOfWork.Option == UnitOfWorkOption.Suppress)
            {
                var writeConnection = Context.GetConnection();
                object obj2 = null;
                try
                {
                    writeConnection.Open();
                    obj2 = writeConnection.Insert(item, null, null);
                }
                finally
                {
                    writeConnection.Close();
                }
                return obj2;
            }
            {
                Func<IDbConnection, IDbTransaction, object, object> action =
                    (conn, tran, oitem) => conn.Insert((TEntity) oitem, tran, null);
                var mapper = DapperExtensions.DapperExtensions.GetMap<TEntity>();
                var properties = mapper.Properties;
                var propertyMap =
                    properties.Where(p => p.Name == "Id").SingleOrDefault(p => p.ColumnName.IndexOf('.') != -1);
                unitOfWork.ChangeOperator(action, Context, item, propertyMap);
                return null;
            }
        }

        /// <summary>
        ///     更新实体
        /// </summary>
        /// <param name="item"></param>
        /// <param name="unitOfWork"></param>
        protected void Modify(TEntity item, UnitOfWork unitOfWork = null)
        {
            if (unitOfWork == null ||
                unitOfWork.Option == UnitOfWorkOption.Suppress)
            {
                var writeConnection = Context.GetConnection();
                try
                {
                    writeConnection.Open();
                    writeConnection.Update(item, null, null);
                }

                finally
                {
                    writeConnection.Close();
                }
            }
            else
            {
                Func<IDbConnection, IDbTransaction, object, object> action =
                    (conn, tran, entity) => conn.Update(item, tran, null);
                unitOfWork.ChangeOperator(action, Context);
            }
        }

        /// <summary>
        ///     删除实体
        /// </summary>
        /// <param name="item"></param>
        /// <param name="unitOfWork"></param>
        protected void Remove(TEntity item, UnitOfWork unitOfWork = null)
        {
            if (unitOfWork == null ||
                unitOfWork.Option == UnitOfWorkOption.Suppress)
            {
                var writeConnection = Context.GetConnection();
                try
                {
                    writeConnection.Open();
                    writeConnection.Delete(item, null, null);
                }

                finally
                {
                    writeConnection.Close();
                }
            }
            else
            {
                Func<IDbConnection, IDbTransaction, object, object> action =
                    (conn, tran, entity) => conn.Delete(item, tran, null);
                unitOfWork.ChangeOperator(action, Context);
            }
        }

        #endregion

        #region 查

        /// <summary>
        ///     获取所有实体
        /// </summary>
        /// <returns></returns>
        protected IEnumerable<TEntity> GetAll()
        {
            var tablename = GetMap().TableName;
            var querysql = DapperHelper.CreateColSqlByMap<TEntity>();
            return GetList($"SELECT {querysql} FROM {tablename}");
        }


        /// <summary>
        ///     根据id获取实体
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        protected TEntity GetEntityById(dynamic id)
        {
            var entity = default(TEntity);
            DapperHelper.NoLockInvoke(
                delegate(IDbConnection conn, IDbTransaction tran)
                {
                    entity =
                        (TEntity)
                        DapperExtensions.DapperExtensions.Get<TEntity>(conn, id, tran, null);
                }, Context);
            return entity;
        }


        protected IEnumerable<TEntity> GetList(string sql, dynamic parameters = null)
        {
            if (IsChangeSqlString(sql))
                throw new Exception("sql语句错误,执行的是查询方法,但SQL语句包含更新代码,SQL语句:" + sql);
            IEnumerable<TEntity> list = null;
            DapperHelper.NoLockInvoke(
                delegate(IDbConnection conn, IDbTransaction tran)
                {
                    list = (IEnumerable<TEntity>) SqlMapper.Query<TEntity>(conn, sql, parameters, tran);
                }, Context);
            return list;
        }

        /// <summary>
        /// 根据SQL获取实体列表
        /// </summary>
        /// <typeparam name="U"></typeparam>
        /// <param name="sql"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public IEnumerable<U> GetList<U>(string sql, dynamic parameters = null)
        {
            if (this.IsChangeSqlString(sql))
            {
                throw new Exception("sql语句错误,执行的是查询方法,但SQL语句包含更新代码,SQL语句:" + sql);
            }
            IEnumerable<U> list = null;
            DapperHelper.NoLockInvoke(delegate (IDbConnection conn, IDbTransaction tran)
            {
                list = (IEnumerable<U>)SqlMapper.Query<U>(conn, sql, (dynamic)parameters, tran);
            }, Context);
            return list;
        }

        /// <summary>
        /// 获取单个标量值
        /// </summary>
        /// <typeparam name="U"></typeparam>
        /// <param name="sql"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        protected U? GetDataByScalar<U>(string sql, dynamic parameters = null) where U : struct
        {
            if (this.IsChangeSqlString(sql))
            {
                throw new Exception("sql语句错误,执行的是查询方法,但SQL语句包含更新代码,SQL语句:" + sql);
            }
            Nullable<U> value = null;
            Type type = typeof(U);
            DapperHelper.NoLockInvoke(delegate (IDbConnection conn, IDbTransaction tran)
            {
                IEnumerable<object> values = SqlMapper.Query(conn, type, sql, (dynamic)parameters, tran);
                value = (Nullable<U>)values.SingleOrDefault();
            }, Context);
            return value;
        }

        /// <summary>
        ///     判断是否是改变数据库的SQL dcl
        /// </summary>
        /// <param name="sql"></param>
        /// <returns></returns>
        private bool IsChangeSqlString(string sql)
        {
            sql = sql.Trim().ToLower();
            return Regex.IsMatch(sql, "^(update|delete|insert|create|alter|drop|truncate)") ||
                   Regex.IsMatch(sql, @"^(select)\s+(into)\s");
        }

        /// <summary>
        /// 执行SQL,映射实体
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="parameters"></param>
        protected void Execute(string sql, dynamic parameters = null, UnitOfWork unitOfWork = null)
        {
            Func<IDbConnection, IDbTransaction, object, object> action = null;
            if (unitOfWork == null ||
                           unitOfWork.Option == UnitOfWorkOption.Suppress)
            {
                IDbConnection writeConnection = this.Context.GetConnection();
                try
                {
                    writeConnection.Open();
                    SqlMapper.Execute(writeConnection, sql, (dynamic)parameters);
                }
                catch (Exception ex)
                {
                    throw new Exception("", ex);
                }
                finally
                {
                    writeConnection.Close();
                }
            }
            else
            {
                if (action == null)
                {
                    action = (conn, tran, entity) => SqlMapper.Execute(conn, sql, (dynamic)parameters, tran);
                }
                unitOfWork.ChangeOperator(action, this.Context);
            }
        }


        #endregion
    }
}